#!/usr/bin/python

# Copyright (c) 2013 EMC Corporation
# All Rights Reserved
#
# This software contains the intellectual property of EMC Corporation
# or is licensed to EMC Corporation from third parties.  Use of this
# software and the intellectual property contained therein is expressly
# limited to the terms and conditions of the License Agreement under which
# it is provided by or on behalf of EMC.

"""
Module to execute remote commands on all the hosts that are given in the input
"""

import commands
import fcntl
import os
import struct
import socket
import sys
import threading
from optparse import OptionParser

CTRL_NODES="/opt/storageos/conf/ctrl_nodes"
DATA_NODES="/opt/storageos/conf/data_nodes"
DEFAULT_PRIV_KEY="/home/swift/.ssh/id_rsa"

def ssh(user, host, remt_cmd, timeout=15, opt="", retry_count=5):
    
    for i in xrange(retry_count):
        cmd = ('ssh  %s -i %s -o StrictHostKeyChecking=no -o ConnectTimeout=%d -l %s'
               ' %s "%s"' %(opt, DEFAULT_PRIV_KEY, timeout, user, host, remt_cmd))
        status, output = commands.getstatusoutput(cmd)
        if status==0:
            return (status, output)     

    return (status, output)     

def get_interface_ip(ifname):
    # code from SO
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    return socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915,
            struct.pack('256s', ifname[:15]))[20:24])


def read_hosts_file(fname):
    """ Create a list of host_names or ip's from the file
        The file should have one and only one hostname or ip per line
    """    

    hosts_lst=[]
    try:
        f = open(fname, 'r')  
    except (IOError, os.error), msg:
        print msg
    else:
        for line in f:
            line = line.strip()
            if line.startswith("#") or line=="":
                continue
            else:
                hosts_lst.append(line)

    return hosts_lst


class exec_cmd(threading.Thread):
    """ Run remote command
    """
    def __init__(self, host, cmd, print_ret_val = False):
        threading.Thread.__init__(self)
        self.cmd=cmd
        self.host=host
        self.ret = []
    def run(self):
        user = "swift"
        ret, out = ssh(user, self.host, self.cmd, opt="-q", retry_count=2)
        print "\nOutput from host : %s \n%s" %(self.host, out)
        if print_ret_val:
            print "\nReturn value from host %s is %d" %(self.host, ret)
         

def rexec(hosts, cmd, serial = False, print_ret_val = False):
   
    threads = []
    for h in hosts:
        t = exec_cmd(h, cmd, print_ret_val)
        threads.append(t) 
        t.start()
        if serial:
            t.join()
    
    # wait for the threads to finish
    for t in threads:
        t.join()

if __name__ =='__main__':

    usage = r"""
Usage:
    %prog [options] "cmd"

    cmd can be any linux or bourne command
    """ 

    parser = OptionParser(usage=usage)
    parser.add_option("-f", "--hosts_file", dest="filename",
                      help="Read data from FILENAME. Each line in the file should "\
                            " be a valid host name")
    parser.add_option("-c", "--ctrl-nodes", dest="ctrl_nodes", action="store_true",
                      help="Run 'cmd' only on control nodes. This option is valid "
                            "only when there is a separate ctrl_nodes hosts file")
    parser.add_option("-d", "--data-nodes", dest="data_nodes", action="store_true",
                      help="Run 'cmd' only on data nodes. This option is valid"
                            "only when there is a separate data_nodes hosts file")
    parser.add_option("-i", "--serial", action="store_true",
                      help="Run command one node at a time")
    parser.add_option("-n", "--hosts", dest="hosts",
                      help="Comma separated list of host names or IP addresses")
    parser.add_option("-X", "--except", dest="notme", action="store_true",
                      help="Skip invocation of the command on the host running this program")
    parser.add_option("-e", dest="prtret", action="store_true",
                      help="Print the return value of the command executed on the hosts")
    
   # parser.add_option("-v", "--verbose",
   #                   action="store_true", dest="verbose")
   # parser.add_option("-q", "--quiet",
   #                   action="store_false", dest="verbose")
    
    (options, args) = parser.parse_args()
    if len(args) == 0:
        parser.print_help()
        sys.exit(-1)
    
    serial = False
    if options.serial:
        serial=True

    # Build the target list
    hosts = []
    if options.filename:
        hosts = read_hosts_file(options.filename)
    elif options.hosts:
        hosts = options.hosts.split(',')

    if not hosts:
        print "No hosts detected"
        print usage
        sys.exit(1)

    # filter out myself if requested
    if options.notme:
        myhost = socket.gethostname()
        myip = get_interface_ip("eth0")
        hosts = [item for item in hosts if (item!=myhost and item!=myip)]

    # check the print return value flag
    print_ret_val = False
    if options.prtret:
        print_ret_val = True

    # run the command
    rexec(hosts, " ".join(args), serial, print_ret_val)

